package openjdk.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * Type involving Generics (ParameterizedType)
 */
public class GetGenericTypeExample3 {
    private List<String> aList;
    private Map<String, List<String>> aMap;

    public static void main(String... args) throws NoSuchFieldException {
        System.out.println("Example: List<String> aList;");
        Field field = GetGenericTypeExample3.class.getDeclaredField("aList");
        Type type = field.getGenericType();
        printType(type, 1);

        System.out.println("Example: Map<String, List<String>> aMap;");
        Field field2 = GetGenericTypeExample3.class.getDeclaredField("aMap");
        Type type2 = field2.getGenericType();
        printType(type2, 1);
    }

    private static void printType(Type type, int level) {
        if (type == null) {
            print(level, "The Type is null");
            return;
        }
        print(level, "Type class=" + type.getClass());
        if (type.getClass() != Class.class) {
            print(level, "Type class implementing interfaces=" +
                    Arrays.toString(type.getClass().getInterfaces()));
        }
        print(level, "Type name= " + type.getTypeName());
        printParameterizedType(type, level);
    }

    private static void printParameterizedType(Type type, int level) {
        if (type instanceof ParameterizedType) {
            print(level, "Casting to ParameterizedType");
            ParameterizedType pt = (ParameterizedType) type;
            print(level, "Using ParameterizedType#getOwnerType()");
            Type ownerType = pt.getOwnerType();
            printType(ownerType, level + 1);

            print(level, "Using ParameterizedType#getRawType()");
            Type rawType = pt.getRawType();
            printType(rawType, level + 1);

            print(level, "Using ParameterizedType#getActualTypeArguments()");
            for (Type actualType : pt.getActualTypeArguments()) {
                print(level, "ActualTypeArgument loop");
                printType(actualType, level + 1);
            }
        }
    }

    private static void print(int level, String string) {
        System.out.printf("%" + (level * 4 - 3) + "s\u00A6- %s%n", "", string);
    }
}
